#include "stdafx.h"
#include "Effect.h"

#include "Log.h"
#include "Exception.h"
#include "ErrorHandlers.h"
#include "MemUtils.h"
#include "Engine.h"

namespace Cvekas {

Effect::Effect(ResourceId id, D3DDevicePtr device, const std::string& name, const std::string& filename)
	:
Resource(name, id),
device(device)
{
	DWORD flags = D3DXFX_NOT_CLONEABLE;
#ifdef _DEBUG
	flags |= D3DXSHADER_DEBUG;
#endif
	
	ID3DXBuffer* errors;

	LOG(FORMAT("Compiling effect %s from file %s ...", % name % filename));

	bool result = E_B(D3DXCreateEffectFromFile(device.get(), filename.c_str(), NULL, NULL, flags, NULL, &effect, &errors));		
	
	if(errors && errors->GetBufferSize() > 1)
	{
		std::string msg = "";
		const char* error = reinterpret_cast<const char*>(errors->GetBufferPointer());
		msg += error;
		LOG(msg);
		
		throw EXCEPTION("Unable to compile effect");
	}

	if(!result)
		throw EXCEPTION("Unable to compile effect");

	is_ready = true;

	auto_params = EffectParamsPtr(new EffectParams(this));

	setUpAutoParams();
}

/*
Effect::Effect(ResourceId id, DevicePtr _device, const std::string& name, const char* effect)
	:
Resource(name, id),
device(_device)
{
	DWORD flags = D3DXFX_NOT_CLONEABLE;
#ifdef _DEBUG
	flags |= D3DXSHADER_DEBUG;
#endif
	
	ID3DXBuffer* errors;

	LOG(str(boost::format("Compiling effect %s ...") % name));

	bool result = E_B(D3DXCreateEffect(device.get(), reinterpret_cast<LPCVOID>(effect.c_str()), effect.length(), 
		NULL, NULL, flags, NULL, &effect, &errors);
	
	if(errors && errors->GetBufferSize() > 1)
	{
		std::string msg = "";
		const char* error = reinterpret_cast<const char*>(errors->GetBufferPointer());
		LOG(msg);
		
		throw EXCEPTION("Unable to compile effect");
	}

	if(!result)
		throw EXCEPTION("Unable to compile effect");
}
*/

Effect::~Effect()
{
	safeRelease(effect);
	is_ready = false;
	LOG(FORMAT("Effect %s released", % name));
}

ParamHandle Effect::getParamByName(const std::string& name)
{
	return (ParamHandle)effect->GetParameterByName(NULL, name.c_str());
}

ParamHandle Effect::getParamBySemantic(const std::string& semantic)
{
	return (ParamHandle)effect->GetParameterBySemantic(NULL, semantic.c_str());
}

void Effect::setParamValue(ParamHandle handle, bool value)
{
	E_E(effect->SetBool((D3DXHANDLE)handle, value));
}

void Effect::setParamValue(ParamHandle handle, float value)
{
	E_E(effect->SetFloat((D3DXHANDLE)handle, value));
}

void Effect::setParamValue(ParamHandle handle, int value)
{
	E_E(effect->SetInt((D3DXHANDLE)handle, value));
}

void Effect::setParamValue(ParamHandle handle, const Matrix& value)
{
	E_E(effect->SetMatrix((D3DXHANDLE)handle, &value));
}

void Effect::setParamValue(ParamHandle handle, TexturePtr value)
{
	E_E(effect->SetTexture((D3DXHANDLE)handle, value->getD3DTexture()));
}

void Effect::setParamValue(ParamHandle handle, const Vector4& value)
{
	E_E(effect->SetVector((D3DXHANDLE)handle, &value));
}

void Effect::setParamValue(ParamHandle handle, const void* ptr, uint size)
{
	E_E(effect->SetValue(handle, ptr, size));
}

unsigned int Effect::begin()
{
	uint passes;
	E_E(effect->Begin(&passes, 0));
	return passes;
}

void Effect::end()
{
	E_E(effect->End());
}

void Effect::beginPass(uint pass)
{
	E_E(effect->BeginPass(pass));
}

void Effect::endPass()
{
	E_E(effect->EndPass());
}

void Effect::setTechnique(uint index)
{
	D3DXHANDLE technique_handle = effect->GetTechnique(index);
	E_E(effect->SetTechnique(technique_handle));
}

void Effect::commitChanges()
{
	E_E(effect->CommitChanges());
}

void Effect::applyAutoParams()
{
	auto_params->applyParams();
}

ID3DXEffect* Effect::getD3DXEffect()
{
	if(is_ready)
		return effect;
	else
		throw EXCEPTION("Effect is not ready for usage");
}

void Effect::onDeviceLost()
{
	E_E(effect->OnLostDevice());
	is_ready = false;
}

void Effect::onDeviceReset()
{
	E_E(effect->OnResetDevice());
	is_ready = true;
}

#define AUTO_PARAM(semantic, value) \
{ \
	ParamHandle handle = getParamBySemantic(semantic); \
	if(handle) auto_params->addDynamic(handle, &value); \
}

void Effect::setUpAutoParams()
{
	Engine e;
	RenderingParams& render_params = e.getVideoDevice()->getRenderingParams();

	AUTO_PARAM("WORLD", render_params.world);
	AUTO_PARAM("VIEW", render_params.view);
	AUTO_PARAM("PROJECTION", render_params.projection);
	AUTO_PARAM("WORLD_VIEW", render_params.world_view);
	AUTO_PARAM("VIEW_PROJECTION", render_params.view_projection);
	AUTO_PARAM("WORLD_VIEW_PROJECTION", render_params.world_view_projection);

	AUTO_PARAM("CAMERA_POS", render_params.camera_pos);
	AUTO_PARAM("CAMERA_DIR", render_params.camera_dir);
	AUTO_PARAM("CAMERA_UP", render_params.camera_up);

	AUTO_PARAM("RT_SIZE", render_params.rendertarget_size);

	AUTO_PARAM("PROJECTION_FOV", render_params.projection_fov);
	AUTO_PARAM("CLIP_NEAR", render_params.clip_near);
	AUTO_PARAM("CLIP_FAR", render_params.clip_far);

	AUTO_PARAM("TIME", render_params.time);

	AUTO_PARAM("PASS", render_params.pass);
}

} // namespace
